const path = require("path");
const os = require("os");
const fs = require("fs-extra");

const { API_PATH, REQUEST_METHODS_ORDER } = require("./config");
const { getPramsComments, getMethodStr, getMethod, convertType } = require("./util");

/**
 * @typedef {object} 200
 * @property {object} schema
 * @property {string} schema.originalRef
 */
function generateApi(paths, moduleMap, definitions) {
  const apiMap = {};

  for (const url in paths) {
    const apiInfo = paths[url];
    const methodObj = getMethod(apiInfo);
    if (methodObj) {
      const [method, methodInfo] = methodObj;
      const { tags, summary, parameters, consumes, responses, description: author } = methodInfo;
      const moduleName = tags[0];
      const modulePath = moduleMap[moduleName];

      if (modulePath) {
        const apiFilePath = path.join(API_PATH, `${modulePath}.js`);
        const apiSummary = moduleName + summary;
        const response200TypeDef = responses[200]?.schema?.originalRef;
        const contentType = consumes?.[0];
        const params = createApiParams(method, url, parameters, definitions);
        const module = createApiModule(apiMap, modulePath, apiFilePath, moduleName);
        module.apis.push({ url, method, params, apiSummary, contentType, response200TypeDef, author });
        apiMap[modulePath] = module;
      }
    }
  }

  generateApiFile(apiMap, definitions);
}

function createApiParams(method, url, parameters, definitions) {
  if (method === "get") {
    return createGetApiParams(url, parameters);
  } else if (method === "post") {
    return createPostApiParams(url, parameters, definitions);
  } else {
    console.log(`不支持的方法${{ url, method }}`);
  }
}

function createGetApiParams(url, parameters) {
  const params = [];
  if (parameters) {
    parameters.forEach((item) => {
      const { name, in: _in, description, required, type, format, items } = item;
      if (_in === "query" || _in === "path") {
        params.push({ name, description, required, type, format, _in, items });
      } else {
        console.log("未处理的情况", { url, parameters });
      }
    });
  }
  return params;
}

function createPostApiParams(url, parameters, definitions) {
  const params = [];
  if (parameters) {
    parameters.forEach((item) => {
      const { name, in: _in, description, required, type, format, schema, items } = item;
      if (_in === "query") {
        params.push({ name, description, required, type, format, _in, items });
      } else if (_in === "body" || _in === "formData") {
        if (schema.originalRef) {
          const { originalRef } = schema;
          const definition = definitions[originalRef];
          const properties = definition.properties || {};
          params.push(
            ...Object.keys(properties).map((key) => {
              const { description, required, type, format, items, enum: _enum, originalRef } = properties[key];
              // 适配以下格式
              // "basicInfo": {
              //   "description": "房屋调查结果登记基本信息",
              //   "$ref": "#/definitions/房屋调查结果登记基本信息传输对象",
              //   "originalRef": "房屋调查结果登记基本信息传输对象"
              // },
              return {
                name: key,
                description,
                required,
                type,
                format,
                items,
                _enum,
                originalRef,
              };
            }),
          );
        } else {
          const { type, format, items } = schema;
          params.push({ name, description, required, type, format, items });
        }
      } else {
        console.log("未处理的情况", { url, parameters });
      }
    });
  }
  return params;
}

function createApiModule(apiMap, modulePath, apiFilePath, moduleName) {
  return (
    apiMap[modulePath] || {
      apiFilePath,
      moduleName,
      apis: [],
    }
  );
}

function generateApiFile(apiMap, definitions) {
  for (const key in apiMap) {
    const module = apiMap[key];
    const { apiFilePath, moduleName, apis } = module;
    if (apis.length > 0) {
      const content = createApiFileContent(moduleName, apis, definitions);
      const data = content.replace(/\n\n$/, "\n").replace(/\n/g, os.EOL);
      fs.writeFileSync(apiFilePath, data, { encoding: "utf-8" });
    } else {
      console.log("apis is empty", module.moduleName);
    }
  }
}

function createApiFileContent(moduleName, apis, definitions) {
  const typeDefContent = getTypeDefContent(apis, definitions);
  const methodContent = getMethodContent(apis, moduleName);
  const importStatement = getImportStatement(methodContent);
  return importStatement + methodContent + typeDefContent;
}

function getMethodContent(apis, moduleName) {
  return apis.reduce((fileContent, cur) => {
    global.url = cur.url;
    const { method, url, params, apiSummary, contentType, author, generic } = cur;
    const paramsComments = getPramsComments(method, params);
    const methodStr = getMethodStr(
      moduleName,
      apiSummary,
      method,
      url,
      paramsComments,
      author,
      generic,
      params,
      contentType,
    );
    return fileContent + methodStr;
  }, "");
}

function getImportStatement(methodContent) {
  const reg = /return (\w+)/g;
  const methods = new Set();
  let result;
  while ((result = reg.exec(methodContent))) {
    methods.add(result[1]);
  }
  return `import { ${[...methods]
    .sort((a, b) => REQUEST_METHODS_ORDER[a] - REQUEST_METHODS_ORDER[b])
    .join(", ")} } from "@/api/request";\n`;
}

function getTypeDefContent(apis, definitions) {
  const typeDefs = apis.reduce((acc, cur) => {
    const { response200TypeDef, params } = cur;
    if (response200TypeDef) {
      const generic = getGeneric(response200TypeDef);
      cur.generic = generic;
      generic.forEach((item) => {
        acc.add(item);
      });
      // 泛型最后一个是否有嵌套
      if (generic.length > 0) {
        const last = generic[generic.length - 1];
        const defs = definitions[last];
        if (defs) {
          const { properties } = defs;
          for (const propertiesKey in properties) {
            const { type, items } = properties[propertiesKey];
            if (type === "array" && items?.originalRef) {
              acc.add(items.originalRef);
            }
          }
        }
      }
    }
    if (params) {
      params.forEach((item) => {
        if (item.type === "array" && item.items?.originalRef) {
          acc.add(item.items.originalRef);
        }
        if (item.originalRef) {
          acc.add(item.originalRef);
        }
      });
    }
    return acc;
  }, new Set());
  return [...typeDefs]
    .sort((a, b) => {
      if (a === "分页返回实体") {
        return -1;
      }
      return a.localeCompare(b);
    })
    .reduce((acc, cur) => {
      const defs = getTypeDefs(cur, definitions);
      acc += defs;
      return acc;
    }, "\n");
}

function getTypeDefs(generic, definitions) {
  if (generic === "array") {
    return "";
  } else {
    if (!["int", "string", "long", "boolean", "object", "Void"].includes(generic)) {
      if (generic === "分页返回实体") {
        return `/**
 * @template T
 * @typedef {Object} 分页返回实体
 * @property {T[]} content - 内容
 * @property {number} elementsNo
 * @property {number} limit
 * @property {number} number
 * @property {number} totalElements
 * @property {number} totalPages
 * @description 分页返回实体
 */

`;
      } else {
        if (!definitions[generic]) {
          console.log(`文档错误: 没有定义 ${generic} 类型`);
          return "";
        }
        const { properties, title, description } = definitions[generic];
        let comments = `/**\n * @typedef {object} ${title}${description ? ` ${description}` : ""}`;
        comments = getCommentStatement(properties, definitions, comments);
        if (description) {
          comments += `\n * @description ${description}`;
        }
        comments += "\n */\n\n";
        // console.log(comments);
        return comments;
      }
    }
    return "";
  }
}

function getCommentStatement(properties, definitions, comments, obj) {
  for (const key in properties) {
    const { type, description, minimum, enum: _enum, originalRef, items } = properties[key];
    if (originalRef) {
      const definition = definitions[originalRef];
      const { properties, type, description } = definition;
      comments += `\n * @property {${convertType(type)}} ${key}${description ? ` ${description}` : ""}`;
      comments += getCommentStatement(properties, definitions, "", key);
    } else {
      if (type === "array") {
        if (items?.originalRef) {
          comments += `\n * @property {array<${items.originalRef}>} ${key}${description ? ` ${description}` : ""}`;
        } else {
          if (items?.type) {
            comments += `\n * @property {array<${convertType(items.type)}>} ${key}${
              description ? ` ${description}` : ""
            }`;
          } else {
            console.log({ key });
            throw new Error("未处理的情况");
          }
        }
      } else {
        comments += `\n * @property {${convertType(type)}} ${obj ? `${obj}.` : ""}${key}${
          description ? ` ${description}` : ""
        }${minimum ? ` 最小值: ${minimum}` : ""}${_enum ? ` 可选值: ${_enum.join(", ")}` : ""}`;
      }
    }
  }
  return comments;
}

function getGeneric(str) {
  const regex = /«([^«»]+)/g;
  const arr = [];
  let result;
  while ((result = regex.exec(str))) {
    let res = result[1];
    if (res === "List") {
      res = "array";
    }
    arr.push(res);
  }
  return arr;
}

module.exports = {
  generateApi,
};
